#pragma once

#include <AVUIRoutedEventArgs.h>
#include <AVUIInputManager.h>

namespace AVUI {

class UIElement;
class Keyboard;
class Control;
class IInputElement;
class KeyboardDevice;

class KeyboardEventArgs : public InputEventArgs
{
public:
    DECLARE_ELEMENT(KeyboardEventArgs, InputEventArgs);

	KeyboardEventArgs(KeyboardDevice* pDevice) : InputEventArgs((InputDevice*)pDevice) { };

    KeyboardDevice* get_KeyboardDevice() { return (KeyboardDevice*)get_Device(); }
};

class KeyEventArgs : public KeyboardEventArgs
{
public:
    DECLARE_ELEMENT(KeyEventArgs, KeyboardEventArgs);

    KeyEventArgs(KeyboardDevice* pDevice, Key::Enum key) : KeyboardEventArgs(pDevice), m_key(key) { }

    Key::Enum get_Key() { return m_key; }
    Key::Enum get_SystemKey() { return m_key; } // TODO -- This is more complicated, lets get back to it if necessary

private:
    Key::Enum m_key;
};

class KeyboardFocusChangedEventArgs : public KeyboardEventArgs
{
public:

    DECLARE_ELEMENT(KeyboardFocusChangedEventArgs, KeyboardEventArgs);

    KeyboardFocusChangedEventArgs(KeyboardDevice* pDevice, DependencyObject* pOldFocus, DependencyObject* pNewFocus) : KeyboardEventArgs(pDevice), m_pOldFocus(pOldFocus), m_pNewFocus(pNewFocus) { }

    DependencyObject* get_OldFocus() { return m_pOldFocus; }
    DependencyObject* get_NewFocus() { return m_pNewFocus; }

private:
    DependencyObject* m_pOldFocus;
    DependencyObject* m_pNewFocus;

};

DEFINE_EVENTHANDLER(Key);
DEFINE_EVENTHANDLER(KeyboardFocusChanged);

class KeyStates
{
public:
    enum Enum
    {
        None = 0,
        Down = 1,
        Toggled = 2
    };
};

/// <summary>Represents the keyboard device. </summary>
class Keyboard : public Object
{
public:
    DECLARE_ELEMENT(Keyboard, Object);

#pragma region Public Methods
    /// <summary>Sets keyboard focus on the specified element.</summary>
    /// <returns>The element with keyboard focus.</returns>
    /// <param name="element">The element on which to set keyboard focus.</param>
    static DependencyObject* Focus(DependencyObject* pElement);

    /// <summary>Gets the set of key states for the specified key.</summary>
    /// <returns>A bitwise combination of the <see cref="T:System.Windows.Input.KeyStates" /> values.</returns>
    /// <param name="key">The specified key.</param>
    static KeyStates::Enum GetKeyStates(Key::Enum key);

    /// <summary>Determines whether the specified key is pressed. </summary>
    /// <returns>true if <paramref name="key" /> is in the down state; otherwise, false.</returns>
    /// <param name="key">The specified key.</param>
    static bool IsKeyDown(Key::Enum key);

    /// <summary>Determines whether the specified key is toggled. </summary>
    /// <returns>true if <paramref name="key" /> is in the toggled state; otherwise, false.</returns>
    /// <param name="key">The specified key.</param>
    static bool IsKeyToggled(Key::Enum key);

    /// <summary>Determines whether the specified key is released. </summary>
    /// <returns>true if <paramref name="key" /> is in the up state; otherwise, false.</returns>
    /// <param name="key">The key to check.</param>
    static bool IsKeyUp(Key::Enum key);

#pragma endregion Public Methods

#pragma region Public Properties

    /// <summary>Gets the element that has keyboard focus. </summary>
    /// <returns>The focused element.</returns>
    static DependencyObject* get_FocusedElement();

    /// <summary>Gets the set of <see cref="T:System.Windows.Input.ModifierKeys" /> that are currently pressed. </summary>
    /// <returns>A bitwise combination of the <see cref="T:System.Windows.Input.ModifierKeys" /> values.</returns>
    static ModifierKeys::Enum get_Modifiers();

    /// <summary>Gets the primary keyboard input device. </summary>
    /// <returns>The device.</returns>
    static KeyboardDevice* get_PrimaryDevice();
        
#pragma endregion Public Properties

#pragma region Public Events

    /// <summary>Identifies the <see cref="E:System.Windows.Input.Keyboard.GotKeyboardFocus" /> attached event.</summary>
    /// <returns>The identifier for the <see cref="E:System.Windows.Input.Keyboard.GotKeyboardFocus" /> attached event.</returns>
    DECLARE_ROUTED_EVENT(GotKeyboardFocus);

    /// <summary>Adds a handler for the <see cref="E:System.Windows.Input.Keyboard.GotKeyboardFocus" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be added.</param>
    static void AddGotKeyboardFocusHandler(DependencyObject* pElement, KeyboardFocusChangedEventHandler* pHandler);

    /// <summary>Removes a handler for the <see cref="E:System.Windows.Input.Keyboard.GotKeyboardFocus" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be removed.</param>
    static void RemoveGotKeyboardFocusHandler(DependencyObject* pElement, KeyboardFocusChangedEventHandler* pHandler);


    /// <summary>Identifies the <see cref="E:System.Windows.Input.Keyboard.KeyDown" /> attached event.</summary>
    /// <returns>The identifier for the <see cref="E:System.Windows.Input.Keyboard.KeyDown" /> attached event.</returns>
    DECLARE_ROUTED_EVENT(KeyDown);

    /// <summary>Adds a handler for the <see cref="E:System.Windows.Input.Keyboard.KeyDown" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be added.</param>
    static void AddKeyDownHandler(DependencyObject* pElement, KeyEventHandler* pHandler);

    /// <summary>Removes a handler for the <see cref="E:System.Windows.Input.Keyboard.KeyDown" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be removed.</param>
    static void RemoveKeyDownHandler(DependencyObject* pElement, KeyEventHandler* pHandler);

    /// <summary>Identifies the <see cref="E:System.Windows.Input.Keyboard.KeyUp" /> attached event.</summary>
    /// <returns>The identifier for the <see cref="E:System.Windows.Input.Keyboard.KeyUp" /> attached event.</returns>
    DECLARE_ROUTED_EVENT(KeyUp);

    /// <summary>Adds a handler for the <see cref="E:System.Windows.Input.Keyboard.KeyUp" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be added.</param>
    static void AddKeyUpHandler(DependencyObject* pElement, KeyEventHandler* pHandler);
 
    /// <summary>Removes a handler for the <see cref="E:System.Windows.Input.Keyboard.KeyUp" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be removed.</param>
    static void RemoveKeyUpHandler(DependencyObject* pElement, KeyEventHandler* pHandler);

    /// <summary>Identifies the <see cref="E:System.Windows.Input.Keyboard.LostKeyboardFocus" /> attached event.</summary>
    /// <returns>The identifier for the <see cref="E:System.Windows.Input.Keyboard.LostKeyboardFocus" /> attached event.</returns>
    DECLARE_ROUTED_EVENT(LostKeyboardFocus);

    /// <summary>Adds a handler for the <see cref="E:System.Windows.Input.Keyboard.LostKeyboardFocus" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be added.</param>
    static void AddLostKeyboardFocusHandler(DependencyObject* pElement, KeyboardFocusChangedEventHandler* pHandler);

    /// <summary>Removes a handler for the <see cref="E:System.Windows.Input.Keyboard.LostKeyboardFocus" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be removed.</param>
    static void RemoveLostKeyboardFocusHandler(DependencyObject* pElement, KeyboardFocusChangedEventHandler* pHandler);

    /// <summary>Identifies the <see cref="E:System.Windows.Input.Keyboard.PreviewGotKeyboardFocus" /> attached event.</summary>
    /// <returns>The identifier for the <see cref="E:System.Windows.Input.Keyboard.PreviewGotKeyboardFocus" /> attached event.</returns>
    DECLARE_ROUTED_EVENT(PreviewGotKeyboardFocus);

    /// <summary>Adds a handler for the <see cref="E:System.Windows.Input.Keyboard.PreviewGotKeyboardFocus" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be added.</param>
    static void AddPreviewGotKeyboardFocusHandler(DependencyObject* pElement, KeyboardFocusChangedEventHandler* pHandler);

	/// <summary>Removes a handler for the <see cref="E:System.Windows.Input.Keyboard.PreviewGotKeyboardFocus" /> attached event. </summary>
	/// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
	/// <param name="handler">The event handler to be removed.</param>
	static void RemovePreviewGotKeyboardFocusHandler(DependencyObject* pElement, KeyboardFocusChangedEventHandler* pHandler);

    /// <summary>Identifies the <see cref="E:System.Windows.Input.Keyboard.PreviewKeyDown" /> attached event.</summary>
    /// <returns>The identifier for the <see cref="E:System.Windows.Input.Keyboard.PreviewKeyDown" /> attached event.</returns>
    DECLARE_ROUTED_EVENT(PreviewKeyDown);

    /// <summary>Adds a handler for the <see cref="E:System.Windows.Input.Keyboard.PreviewKeyDown" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be added.</param>
    static void AddPreviewKeyDownHandler(DependencyObject* pElement, KeyEventHandler* pHandler);
 
    /// <summary>Removes a handler for the <see cref="E:System.Windows.Input.Keyboard.PreviewKeyDown" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be removed.</param>
    static void RemovePreviewKeyDownHandler(DependencyObject* pElement, KeyEventHandler* pHandler);

    /// <summary>Identifies the <see cref="E:System.Windows.Input.Keyboard.PreviewKeyUp" /> attached event.</summary>
    /// <returns>The identifier for the <see cref="E:System.Windows.Input.Keyboard.PreviewKeyUp" /> attached event.</returns>
    DECLARE_ROUTED_EVENT(PreviewKeyUp);

    /// <summary>Adds a handler for the <see cref="E:System.Windows.Input.Keyboard.PreviewKeyUp" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be added.</param>
    static void AddPreviewKeyUpHandler(DependencyObject* pElement, KeyEventHandler* pHandler);

    /// <summary>Removes a handler for the <see cref="E:System.Windows.Input.Keyboard.PreviewKeyUp" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be removed.</param>
    static void RemovePreviewKeyUpHandler(DependencyObject* pElement, KeyEventHandler* pHandler);

    /// <summary>Identifies the <see cref="E:System.Windows.Input.Keyboard.PreviewLostKeyboardFocus" /> attached event.</summary>
    /// <returns>The identifier for the <see cref="E:System.Windows.Input.Keyboard.PreviewLostKeyboardFocus" />  attached event.</returns>
    DECLARE_ROUTED_EVENT(PreviewLostKeyboardFocus);

    /// <summary>Adds a handler for the <see cref="E:System.Windows.Input.Keyboard.PreviewLostKeyboardFocus" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be added.</param>
    static void AddPreviewLostKeyboardFocusHandler(DependencyObject* pElement, KeyboardFocusChangedEventHandler* pHandler);

    /// <summary>Removes a handler for the <see cref="E:System.Windows.Input.Keyboard.PreviewLostKeyboardFocus" /> attached event. </summary>
    /// <param name="element">The <see cref="T:System.Windows.UIElement" /> or <see cref="T:System.Windows.ContentElement" /> that listens to this event.</param>
    /// <param name="handler">The event handler to be removed.</param>
    static void RemovePreviewLostKeyboardFocusHandler(DependencyObject* pElement, KeyboardFocusChangedEventHandler* pHandler);

#pragma endregion Public Events

public:

#pragma region Internal Methods

    static bool IsFocusable(DependencyObject* pElement);
    static bool IsValidKey(Key::Enum key);

#pragma endregion Internal Methods
 
private:
    static void StaticConstructor();
};


}; // namespace AVUI
